/**
 * @file Define utility functions for global use
 * 
 * @author Steve Gao<gaofu@parllay.com>
 */
const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
const request = require('request');
const urlParse = require('url').parse;

const URI = require('urijs');

const wxCrypto = require('./wechat-crypto');
const { Configuration } = require('./constants');
const { RedisError, MySQLError, WeChatError } = require('./errors');
const csv = require('csv');

//function encryptContent(xml){
//    cryptor.encryptMsg();
//}
//exports.encryptMsg = encryptContent;

//function encryptMessage(context){
function encryptMessage(context, params, message) {
    var wechatConfig = context.getConfiguration(Configuration.WECHAT);
    var cryptor = new wxCrypto(wechatConfig.token, wechatConfig.component_app_id, wechatConfig.aes_key);
    var message = cryptor.encryptMsg(message, params);
    return message;
}

exports.encryptMessage = encryptMessage;

function decryptMessage(context, params, message) {
    var wechatConfig = context.getConfiguration(Configuration.WECHAT);
    var cryptor = new wxCrypto(wechatConfig.token, wechatConfig.component_app_id, wechatConfig.aes_key);
    var message = cryptor.decryptMsg(params.msg_signature, params.timestamp, params.nonce, message);
    return message;
}

exports.decryptMessage = decryptMessage;

function sendRequest(url, method, headers, data, callback) {
    var contents = JSON.stringify(data);
    var urlObj = urlParse(url);
    headers['Content-Length'] = Buffer.isBuffer(contents) ? contents.length : Buffer.byteLength(contents);

    var options = {
        type: urlObj.protocol.replace(':', ''),
        host: urlObj.host,
        port: urlObj.port || (this.type == 'http' ? 80 : 443),
        path: urlObj.path + (urlObj.search ? urlObj.search : ""),
        method: method,
        headers: headers
    };

    var req = require(options.type == 'http' ? 'http' : 'https').request(options, function (res) {
        //var bufferHelper = new BufferHelper();
        res.setEncoding('utf8');
        var result = '';
        res.on('data', function (data) {
            result += data
        });

        res.on('end', function () {
            callback(result);
        });
    });

    req.write(contents);
    req.end();
}

exports.sendRequest = sendRequest;

function curlPost(url, data, callback) {
    request.post({
        url: url,
        form: JSON.stringify(data),
        encoding: 'utf8'
    }, (error, response, body) => {
        callback(error, body);
    });
}

exports.curlPost = curlPost;

exports.uuid = function () {
    return (Math.round(Math.random() * 100000000000000000000)).toString(36);
};

exports.generateIdentifier = function (length, category) {
    length = length || 10;
    category = category || 'lowercase'; //full|lowercase|uppercase
    var characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    switch (category) {
        case 'lowercase':
            characters = characters.substr(10, 26);
            break;
        case 'uppercase':
            characters = characters.substr(36);
            break;
    }
    var seedSeries = [];
    while (length > 0) {
        var seed = Math.random();
        var index = Math.ceil(seed * characters.length) - 1;
        var hit = characters.charAt(index);
        if (hit.charCodeAt(0) > 0) {
            length--;
            seedSeries.push(hit)
        }
    }

    return seedSeries.join('');
};

function saveTempFile(base64Data) {
    var date = new Date();
    var year = date.getFullYear();
    var month = date.getMonth() + 1;
    var day = date.getDate();
    var hour = date.getHours();
    var pathKey = '' + date.getFullYear() + (month < 10 ? '0' + month : month) + (day < 10 ? '0' + day : day) + (hour < 10 ? '0' + hour : hour);
    var regExp = /^data:image\/(\w+);base64,/;
    var matches = regExp.exec(base64Data);
    var extension = '.dat';
    if (matches && matches.length >= 2) {
        extension = matches[1];
    }

    var fileKey = this.uuid() + '.' + extension;
    var subpath = '/static/uploads/' + pathKey;
    var filepath = path.join(process.cwd(), subpath);
    if (!fs.existsSync(filepath)) {
        fs.mkdirSync(filepath);
    }

    base64Data = base64Data.replace(regExp, "");
    var dataBuffer = new Buffer(base64Data, 'base64');
    var realFile = path.join(filepath, fileKey);

    fs.writeFileSync(realFile, dataBuffer);

    return {
        fileKey: path.join(pathKey, fileKey),
        realFile: realFile
    };
}

exports.saveTempFile = saveTempFile;

function saveAssetsFile(data) {
    var extension;
    var date = new Date();
    var year = date.getFullYear();
    var month = date.getMonth() + 1;
    var day = date.getDate();
    var hour = date.getHours();
    var pathKey = '' + date.getFullYear() + (month < 10 ? '0' + month : month) + (day < 10 ? '0' + day : day) + (hour < 10 ? '0' + hour : hour);
    if (data[0].mimetype == 'video/mp4') {
        extension = data[0].originalname.split('.').pop().toLowerCase();
    } else if (data[0].mimetype.split('/')[0] == 'image') {
        extension = data[0].originalname.split('.').pop().toLowerCase();
    } else {
        return {
            success: false,
            error: '格式错误'
        }
    }
    var fileKey = this.uuid() + '.' + extension;
    var subPath = '/static/uploads/' + pathKey;
    var filePath = path.join(process.cwd(), subPath);
    var type = data[0].fieldname;
    if (!fs.existsSync(filePath)) {
        fs.mkdirSync(filePath);
    }
    var realFile = path.join(filePath, fileKey);
    fs.writeFileSync(realFile, data[0].buffer);
    return {
        success: true,
        fileKey: path.join(pathKey, fileKey),
        realFile: realFile,
        type: type
    }
}

exports.saveAssetsFile = saveAssetsFile;

function verifyEmailAddress(email) {
    var emailRegEx = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
    if (emailRegEx.test(email)) {
        return true;
    }
    return false;
}

exports.verifyEmailAddress = verifyEmailAddress;

function md5(plainText) {
    var md5Cipher = crypto.createHash('md5');

    md5Cipher.update(str);

    var encryptedText = md5Cipher.digest(plainText);

    return encryptedText;
}

exports.md5 = md5;

function getRemoteIP(request) {
    return request.headers['x-real-ip']
        || request.headers['x-forwarded-for']
        || request.connection.remoteAddress
        || request.socket.remoteAddress
        || 'unknown';
}

exports.getRemoteIP = getRemoteIP;

function saveVideoConfig(data) {

}
exports.saveVideoConfig = saveVideoConfig;

function getEmailUserName(email) {
    return email.substr(0, email.indexOf('@'));
}
exports.getEmailUserName = getEmailUserName;

/**
 * Get WeChat create time
 * 
 * @returns
 */
function getWeChatCreateTime() {
    return Math.round(Date.now() / 1000);
}

exports.getWeChatCreateTime = getWeChatCreateTime;

var checkNumberParameter = function (arg, argName) {
    if (typeof arg !== 'number') {
        throw new Error(argName + " is invalid. Expected a number, but found it to be: " + arg);
    }
};
exports.checkNumberParameter = checkNumberParameter;

var checkStringParameter = function (arg, argName) {
    if (typeof arg !== 'string') {
        throw new Error(argName + " is invalid. Expected a string, but found it to be: " + arg);
    }
};
exports.checkStringParameter = checkStringParameter;

var checkBooleanParameter = function (arg, argName) {
    if (typeof arg !== 'boolean') {
        throw new Error(argName + " is invalid. Expected a boolean, but found it to be: " + arg);
    }
};
exports.checkBooleanParameter = checkBooleanParameter;

var checkFunctionParameter = function (arg, argName) {
    if (!arg || Object.prototype.toString.call(arg) != '[object Function]') {
        throw new Error(argName + " is invalid. Expected a function, but found it to be: " + arg);
    }
};
exports.checkFunctionParameter = checkFunctionParameter;

var checkArrayParameter = function (arg, argName) {
    if (!arg || Object.prototype.toString.call(arg) != '[object Array]') {
        throw new Error(argName + " is invalid. Expected an array, but found it to be: " + arg);
    }
};
exports.checkArrayParameter = checkArrayParameter;

var checkObjectParameter = function (arg, argName) {
    if (arg && Object.prototype.toString.call(arg) != '[object Object]') {
        throw new Error(argName + " is invalid. Expected an object, but found it to be: " + arg);
    }
};
exports.checkObjectParameter = checkObjectParameter;

var checkNonEmptyParameter = function (arg, argName) {
    if (!arg) {
        throw new Error(argName + " is invalid. Expected a non-zero, non-empty value, but found it to be: " + arg);
    }
};
exports.checkNonEmptyParameter = checkNonEmptyParameter;

function buildRedisError(error, context) {
    return new RedisError(error.message, error.code, context);
}

exports.buildRedisError = buildRedisError;

function buildMySQLError(error, context) {
    return new MySQLError(error.message, `${error.errno}:${error.code}`, context);
}

exports.buildMySQLError = buildMySQLError;

function getLinks(text) {
    let urls = [];
    text = URI.withinString(text, (url) => {
        urls.push(url);
        return `<a href="${url}">url</a>`;
    });

    return urls;
}

exports.getLinks = getLinks;

function cloneObject(obj) {
    return JSON.parse(JSON.stringify(obj));
}

exports.cloneObject = cloneObject;


/**
 * exportExcel
 * 
 * @param {Object}   request
 * @param {Object}   response
 * @param {String}   fileName
 * @param {Array}    data
 * @param {Function} parentCallback
 */
function exportExcel(request, response, fileName, data, parentCallback) {
    let curFileName = fileName || "parllayTable.xls";
    response.set({
        'Content-Type': 'application/vnd.ms-execl',
        'Content-Disposition': "attachment;filename=" + encodeURIComponent(curFileName),
        'Pragma': 'no-cache',
        'Expires': 0
    });
    let content = '';
    for (let i = 0, len = data.length; i < len; i++) {
        try {
            for (let y = 0; y < data[i].length; y++) {
                content += data[i][y];
                content += '\t';
            }
        }catch(error){
            console.log(error);
        }
        content += '\t\n';
    }
    //此处说需要转换字符集，我先注释了
    // let buffer = new Buffer(content);
    //需要转换字符集  
    // var iconv = require('iconv-lite');
    // var str = iconv.encode(buffer, 'gb2312');
    // response.send(str);
    response.send(content);
}

exports.exportExcel = exportExcel;

/**
 * exportCVS
 * @param {Object}   req
 * @param {Object}   res
 * @param {String}   fileName  下载的文件名称，不传默认为parllayTable.csv
 * @param {Array}    data      处理之后的提供下载的数据
 * @param {Fucntion} parentCallback 暂时没用，后续看需求
 */
function exportCSV(req, res, fileName, data, parentCallback) {
    let curfileName = fileName || "parllayTable.csv";
    res.set({
        'Content-Type': 'application/vnd.ms-execl',
        'Content-Disposition': "attachment;filename=" + curfileName,
        'Pragma': 'no-cache',
        'Expires': 0
    });
    csv.stringify(data)
        .pipe(csv.parse())
        .pipe(csv.stringify())
        .pipe(res);
}

exports.exportCSV = exportCSV;